package com.jlect.swebing.ui.client;

import com.jlect.swebing.ui.client.laf.ComponentUI;
import com.jlect.swebing.util.client.Collections;
import com.jlect.swebing.util.client.Logger;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * The base component in the library
 *
 * @author Sergey Kozmin
 * @since 10.11.2007 22:10:08
 */
public abstract class GComponent implements EligibilityListener,
        ComponentsProvider {
    private static final String uiComponentID = "GComponent";

    public static final String DISABLED_PROPERTY_NAME = "component.disabled";

    private Logger logger = Logger.getLogger(getUiComponentID());//todo remove after debug

    private GComponent parent;
    private List children;

    private Rectangle bounds = new Rectangle(0, 0, 0, 0);

    private Dimension preferredSize;
    private Dimension maximumSize;
    private Dimension minimumSize;
    private float alignmentY;
    private float alignmentX;
    private boolean selected;
    private boolean visible = true;
    private boolean enabled = true;
    private Color fg;
    private Color bg;
    private Font font;
    private String toolTipText;
    private boolean transparent;
    private ComponentOrientation orientation = new ComponentOrientation();

//    private Renderer renderer;
    private ComponentUI ui;

    private PropertyListenerCollection propertyListeners;
    private ComponentListenerCollection componentListeners;
    private MouseListenerCollection mouseListeners;
    private KeyListenerCollection keyListeners;

    /**
     * Component's focus traversal policy, by default it doesn't request any focus
     */
    private FocusTraversalPolicy focusPolicy = NotFocusableTraverslaPolicy
            .getPolicy();


    /**
     * Returns the LAF object that manages view of this component
     *
     * @return component ui object
     */
    public ComponentUI getUI() {
        return ui;
    }

    public void setUI(ComponentUI componentUI) {
        if(ui != null) {
            ui.uninstallUI(this);
        }
        ui = componentUI;
        ui.installUI(this);
    }

    public abstract void updateUI();

    public GComponent getParent() {
        return parent;
    }

    public void setParent(GComponent parent) {
        this.parent = parent;
    }

    public void addComponent(GComponent component) {
        if(component == null) {
            throw new IllegalArgumentException("Could not add null component. ");
        }
        if(children == null) {
            children = new ArrayList();
        }
        component.setParent(this);
        children.add(component);
    }

    public void removeComponent(GComponent component) {
        if(component == null) {
            throw new IllegalArgumentException(
                    "Could not remove null component. ");
        }
        if(children != null) {
            children.remove(component);
            component.setParent(null);
        }
    }

    public GComponent removeComponent(int index) {
        GComponent component = null;
        if(children != null) {
            component = (GComponent) children.remove(index);
            component.setParent(null);
        }
        return component;
    }

    public Iterator getComponentsIterator() {
        return children == null ? Collections.EMPTY_ITERATOR
                : children.iterator();
    }

    public int getComponentsCount() {
        return children == null ? 0: children.size();
    }

    public GComponent getComponentByIndex(int index) {
        return children == null ? null:(GComponent) children.get(index);
    }

    /**
     * Return focus traversal policy
     *
     * @return policy object. must not be null
     */
    public FocusTraversalPolicy getFocusPolicy() {
        return focusPolicy;
    }

    protected void setFocusPolicy(FocusTraversalPolicy focusPolicy) {
        this.focusPolicy = focusPolicy;
    }

    public void requestFocus() {
        FocusManagerProvider.getFocusManager().requestFocus(this);
    }

    public void setPreferredSize(Dimension preferredSize) {
        if(preferredSize == null) {
            throw new IllegalArgumentException("Could not set null object as preffered size");
        }
        this.preferredSize = preferredSize;
    }

    /**
     * Could not return null object
     * @return preffered component size
     */
    public Dimension getPreferredSize() {
        if(preferredSize == null) {
            preferredSize = new Dimension(20, 20);
        }
        return preferredSize;
    }

    public void setMaximumSize(Dimension maximumSize) {
        this.maximumSize = maximumSize;
    }

    public Dimension getMaximumSize() {
        return maximumSize;
    }

    public void setMinimumSize(Dimension minimumSize) {
        this.minimumSize = minimumSize;
    }

    public Dimension getMinimumSize() {
        return minimumSize;
    }

    public void setAlignmentY(float alignmentY) {
        this.alignmentY = alignmentY;
    }

    public float getAlignmentY() {
        return alignmentY;
    }

    public void setAlignmentX(float alignmentX) {
        this.alignmentX = alignmentX;
    }

    public float getAlignmentX() {
        return alignmentX;
    }

    public boolean isVisible() {
        return visible;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public Color getFg() {
        return fg;
    }

    public Color getBg() {
        return bg;
    }

    public Font getFont() {
        return font;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
//        todo transfer selection to renderer 
        this.selected = selected;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    public void setEnabled(boolean enabled) {
        if (this.enabled != enabled) {
            firePropertyChanged(DISABLED_PROPERTY_NAME, Boolean.valueOf(this.enabled), Boolean.valueOf(enabled));
            this.enabled = enabled;
        }
    }

    public void setForeground(Color fg) {
        logger.info("Setting foreground [" + fg.toString() + "]");
        this.fg = fg;
    }

    public void setBackground(Color bg) {
        logger.info("Setting background [" + bg.toString() + "]");
        this.bg = bg;
    }

    public void setFont(Font font) {
        this.font = font;
    }

    public void setToolTipText(String text) {
        this.toolTipText = text;
    }

    public String getToolTipText() {
        return toolTipText;
    }

    public GComponent createToolTip() {
        //        @todo implement
        return null;
    }

    public void scrollRectToVisible(Rectangle aRect) {
        //        @todo implement
    }

    public void setTransferHandler(TransferHandler newHandler) {
        //        @todo implement
    }

    public TransferHandler getTransferHandler() {
        //        @todo implement
        return null;
    }

    public void setSize(Dimension size) {
        setBounds(getX(), getY(), size.width, size.height);
    }

    public Dimension getSize() {
        return bounds != null ? bounds.getSize() : Dimension.ZERO_SIZE_DIMENSION;
    }

    public void setBounds(int x, int y, int width, int height) {
        bounds.setBounds(x, y, width, height);//todo check if bounds changed, fire apropriate event
        if(componentListeners != null) {
            componentListeners.fireComponentResized();
        }
    }

    public void setLocation(int x, int y) {
        bounds.setLocation(x, y);
        if(componentListeners != null) {
            componentListeners.fireComponentMoved();
        }
    }

    public Location getLocation() {
        return bounds.getLocation();
    }

    /**
     * @return component's left position
     */
    public int getX() {
        return bounds.getX();
    }

    /**
     * @return component's top position
     */
    public int getY() {
        return bounds.getY();
    }

    public int getWidth() {
        return bounds.getWidth();
    }

    public int getHeight() {
        return bounds.getHeight();
    }

    public void setTransparent(boolean transparent) {
        this.transparent = transparent;
    }

    public boolean isTransparent() {
        return transparent;
    }
/*
    public void setRenderer(Renderer renderer) {
        if(renderer == null) {
            throw new IllegalArgumentException(
                    "Renderer could not be a null object.");
        }
        this.renderer = renderer;
        renderer.setEligibilityListener(this);
    }

    public Renderer getRenderer() {
        if(renderer == null) {
            setRenderer(ui.createRenderer());
        }
        return renderer;
    }*/

    public void addComponentListener(ComponentListener listener) {
        if(componentListeners == null) {
            componentListeners = new ComponentListenerCollection(this);
        }
        componentListeners.add(listener);
    }

    public void removeComponentListener(ComponentListener listener) {
        if(componentListeners != null) {
            componentListeners.remove(listener);
        }
    }

    public void addMouseListener(MouseListener listener) {
        if(mouseListeners == null) {
            mouseListeners = new MouseListenerCollection();
        }
        mouseListeners.add(listener);
    }

    public void removeMouseListener(MouseListener listener) {
        if(mouseListeners != null) {
            mouseListeners.remove(listener);
        }
    }

    public void addPropertyListener(PropertyChangeListener listener) {
        if(propertyListeners == null) {
            propertyListeners = new PropertyListenerCollection();
        }
        propertyListeners.add(listener);
    }

    public void removePropertyListener(PropertyChangeListener listener) {
        if(propertyListeners != null) {
            propertyListeners.remove(listener);
        }
    }

    protected void firePropertyChanged(String name, Object oldValue, Object newValue) {
        if(propertyListeners != null) {
            propertyListeners.eventAppeared(this, name, oldValue, newValue);
        }
    }

    protected MouseListenerCollection getMouseListeners() {
        return mouseListeners;
    }

    public void addKeyListener(KeyListener listener) {
        if(keyListeners == null) {
            keyListeners = new KeyListenerCollection();
        }
        keyListeners.add(listener);
    }

    public void removeKeyListener(KeyListener listener) {
        if(keyListeners != null) {
            keyListeners.remove(listener);
        }
    }

    protected KeyListenerCollection getKeyListeners() {
        return keyListeners;
    }

    public void componentEligible(GComponent renderer) {
//        ComponentsRegister.getRegister().registerComponent(this);
        if(componentListeners != null) {
            componentListeners.fireComponentShown();
        }
        visible = true;
    }

    public void componentNotEligible(GComponent renderer) {
        if(componentListeners != null) {
            componentListeners.fireComponentHidden();
        }
        visible = false;
//        ComponentsRegister.getRegister().cancelRegistration(this);
    }

    public ComponentOrientation getOrientation() {
        return orientation;
    }

    public void setOrientation(ComponentOrientation orientation) {
        this.orientation = orientation;
    }

    public void focusLost() {
        logger.info("Component focus lost. ");//todo implement listeners
    }

    public void focusGained() {
        logger.info("Component focus gained. ");//todo implement listeners
    }

    protected void fireMouseEvent(MouseEvent event) {
        if(mouseListeners != null) {
            mouseListeners.fireEvent(event);
        }
    }

    protected void fireKeyEvent(KeyEvent event) {
        if(keyListeners != null) {
            keyListeners.eventAppeared(event);
        }
    }

    public abstract String getUiComponentID();
}
